#include "filesource.h"

static int cbOpenInputTimeoutInFile(void *user)
{
    FileSource *fileSource = static_cast<FileSource *>(user);
    if(fileSource != nullptr)
    {
        //默认超时3s结束阻塞
        int64_t msOffset = msOffset = av_gettime_relative() / 1000 - fileSource->m_curIOTime;
        if(msOffset > 3000)
        {
            string info = fileSource->m_param.srcUrl + ":" + fileSource->m_param.app + fileSource->m_param.stream;
            log_printf(LOG_LEV, "cbInputTimeoutInFile %s", info.c_str());
            return 1;
        }
    }

    return 0;
}

static void cbH264FrameInFile(void *user_data, mk_h264_splitter splitter, const char *data, int size)
{
    FileSource *fileSource = static_cast<FileSource *>(user_data);
    if(fileSource == nullptr)
    {
        return;
    }

    //设定时间戳
    uint64_t dts = mk_util_get_current_millisecond();

    //构造数据帧 按照输入不更改编码方式
    mk_frame frame;
    if(!fileSource->m_encodeH264)
    {
        frame = mk_frame_create(MKCodecH265, dts, dts, data, size, NULL, NULL);
    }
    else
    {
        frame = mk_frame_create(MKCodecH264, dts, dts, data, size, NULL, NULL);
    }

//    log_printf(LOG_LEV, "test! %d", dts);

    //测试裸流保存
    if(fileSource->m_enableH264File)
    {
        if(nullptr == fileSource->m_h264File)
        {
            fileSource->m_h264File = fopen("./test.h264", "w+");
            if(!fileSource->m_h264File)
            {
                fileSource->m_enableH264File = false;
                return;
            }

            fileSource->m_h264FileSize = 0;
        }
        else
        {
            const char *frameData = mk_frame_get_data(frame);
            uint32_t frameSize = mk_frame_get_data_size(frame);
            fwrite(frameData, 1, frameSize, fileSource->m_h264File);

            //判断大小
            fileSource->m_h264FileSize += frameSize;
            if(fileSource->m_h264FileSize > 5 * 1024 * 1024)
            {
                fclose(fileSource->m_h264File);
                fileSource->m_h264File = nullptr;
                fileSource->m_enableH264File = false;
            }
        }
    }

    //保证有效
    if((fileSource->m_media && frame))
    {
        mk_media_input_frame(fileSource->m_media, frame);
        mk_frame_unref(frame);
    }
}

FileSource::FileSource()
{

}

FileSource::~FileSource()
{
    stopTask();
}

void FileSource::setParam(const STU_CAMERAINFO &info)
{
    //对于文件其实只有流输出信息有效，其它均采用视频文件本身的参数
    m_param.srcUrl = info.sourceUrl;
    m_param.stream = info.stream;
    m_param.app = info.app;
    m_param.record = info.record;
}

void FileSource::startTask()
{
    if(!m_media || !m_splitter)
    {
        log_printf(LOG_LEV, "m_media m_splitter not ready!");
        return;
    }

    m_bRun.store(true);

    if (m_thread == nullptr)
    {
        m_thread = new std::thread(&FileSource::run, this);
    }
}

void FileSource::stopTask()
{
    m_bRun.store(false);

    if(m_thread != nullptr )
    {
        if(m_thread->joinable())
        {
            m_thread->join();
            log_printf(LOG_LEV, "File thread finished");
        }

        releaseMediaSource();

        delete m_thread;
        m_thread = nullptr;
    }
}

void FileSource::autoReconnect()
{
    log_printf(LOG_LEV, "autoReconnect %s:%s %s", m_param.srcUrl.c_str(), m_param.app.c_str(), m_param.stream.c_str());
    initMediaSource();
    startTask();
}

void FileSource::initMediaSource()
{
    //开始读取设备数据
    AVFormatContext *fmt_ctx = openFile(m_param.srcUrl.c_str());
    if(!fmt_ctx)
    {
        log_printf(LOG_LEV, "inputCtx is null");
        return;
    }

    releaseMediaSource();

    //初始化mk接口
    m_media = mk_media_create("", m_param.app.c_str(), m_param.stream.c_str(), 0, 0, m_param.record);
    if(!m_media)
    {
        log_printf(LOG_LEV, "mk_media_create error");
        return;
    }

    codec_args v_args;
    v_args.video.fps = m_inputFPS;
    v_args.video.height = m_inputHeight;
    v_args.video.width = m_inputWidth;
    mk_track v_track;
    if(!m_encodeH264)
    {
        v_track = mk_track_create(MKCodecH265, &v_args);
    }
    else
    {
        v_track = mk_track_create(MKCodecH264, &v_args);
    }

    mk_media_init_track(m_media, v_track);
    mk_media_init_complete(m_media);
    mk_track_unref(v_track);

    // Create h264 frame splitter
    m_splitter = mk_h264_splitter_create(cbH264FrameInFile, this, !m_encodeH264);
    if(!m_splitter)
    {
        log_printf(LOG_LEV, "mk_h264_splitter_create error");
        return;
    }

    log_printf(LOG_LEV, "initMediaSource Sucess! %d %d %d H264:%d", m_inputFPS, m_inputWidth, m_inputHeight, m_encodeH264);

}

void FileSource::releaseMediaSource()
{
    if(m_media || m_splitter)
    {
        log_printf(LOG_LEV, "releaseMediaSource");
    }

    if(m_media)
    {
        mk_media_release(m_media);
        m_media = nullptr;
    }

    if(m_splitter)
    {
        mk_h264_splitter_release(m_splitter);
        m_splitter = nullptr;
    }
}

AVFormatContext *FileSource::openFile(const char *fileName)
{
    if(m_inFmtCtx)
    {
        avformat_close_input(&m_inFmtCtx);
    }

    m_inFmtCtx = avformat_alloc_context();

    m_curIOTime = av_gettime_relative() / 1000;
    m_inFmtCtx->interrupt_callback.callback = cbOpenInputTimeoutInFile;
    m_inFmtCtx->interrupt_callback.opaque = this;

    int ret = avformat_open_input(&m_inFmtCtx, fileName, nullptr, nullptr);
    if (ret < 0)
    {
        log_printf(LOG_LEV, "avformat_open_input error %s %s", fileName, getFFmpegErrroStr(ret).c_str());
        return nullptr;
    }

    // 查找流信息
    ret = avformat_find_stream_info(m_inFmtCtx, nullptr);
    if (ret < 0)
    {
        getFFmpegError(ret);
        log_printf(LOG_LEV, "avformat_find_stream_info");
        return nullptr;
    }

    //获取视频流
    m_videoIndex = av_find_best_stream(m_inFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (m_videoIndex < 0)
    {
        getFFmpegError(ret);
        log_printf(LOG_LEV, "av_find_best_stream");
        return nullptr;
    }

    //获取视频信息
    AVStream *videoStream = m_inFmtCtx->streams[m_videoIndex];
    AVCodecID decoderID = videoStream->codecpar->codec_id;
    m_inputWidth = videoStream->codecpar->width;
    m_inputHeight = videoStream->codecpar->height;
    m_inputFormat = videoStream->codecpar->format;
    m_inputFPS = av_q2d(videoStream->avg_frame_rate);
    m_encodeH264 = (decoderID == AV_CODEC_ID_H264) ? true : false;
    log_printf(LOG_LEV, "File DecoderInfo %s %d %d %d %d H264=%d", m_param.srcUrl.c_str(), m_inputWidth, m_inputHeight, m_inputFPS, m_inputFormat, m_encodeH264);

    return m_inFmtCtx;
}

void FileSource::run()
{
    m_isRuning = false;

    //开始时间
    int64_t startMs = av_gettime_relative() / 1000;

    //开始取编码帧并编码送进媒体源
    while (m_bRun)
    {
        AVPacket input_pkt;

        int ret = av_read_frame(m_inFmtCtx, &input_pkt);
        if (ret < 0)
        {
            //如果是读取完毕则从头开始
            if (ret == AVERROR_EOF)
            {
                av_seek_frame(m_inFmtCtx, -1, 0, AVSEEK_FLAG_BACKWARD);
                avformat_flush(m_inFmtCtx);
                startMs = m_curIOTime;
                log_printf(LOG_LEV, "av_read_frame EOF! File Play Again!");
                continue;
            }
            else
            {
                log_printf(LOG_LEV, "av_read_frame error %s", getFFmpegErrroStr(ret).c_str());
                break;
            }
        }

        m_curIOTime = av_gettime_relative() / 1000;

        //目前只取视频流
        if(input_pkt.stream_index == m_videoIndex)
        {
            //计算时间间隔
            int elapsed = m_curIOTime - startMs;
            int ptsTime = input_pkt.pts * av_q2d(m_inFmtCtx->streams[m_videoIndex]->time_base) * 1000;
            if(ptsTime > elapsed)
            {
                int offset = ptsTime - elapsed;
                if(offset > 5000)
                {
                    offset = 5000;
                }
                this_thread::sleep_for(std::chrono::milliseconds(offset));
            }

//            log_printf(LOG_LEV, "pkt info %d pts:%d %d %d", input_pkt.size, input_pkt.pts, ptsTime, elapsed);

            input_pkt.data[0] = 0;
            input_pkt.data[1] = 0;
            input_pkt.data[2] = 0;
            input_pkt.data[3] = 1;
            mk_h264_splitter_input_data(m_splitter, (const char *)input_pkt.data, input_pkt.size);
        }

        m_isRuning = true;
        av_packet_unref(&input_pkt);
    }

    // 释放资源
    avformat_close_input(&m_inFmtCtx);

    m_bRun.store(false);
    m_isRuning = false;
    log_printf(LOG_LEV, "run stop");
}
